package mx.vlabs.jpautils.eao.api;

import java.io.Serializable;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.apache.log4j.Logger;

import mx.vlabs.jpautils.eo.EO;

public class EAOQueryBuilder<T extends EO<PK>,PK extends Serializable> {
	
	private Logger log = Logger.getLogger(EAOQueryBuilder.class);
	
	public static final String CONDITIONS_KEY = "conditions";
	public static final String MAX_KEY = "max";
	public static final String OFFSET_KEY = "offset";
	public static final String COUNT_KEY = "count";
	
	private Class<T> entityClass;

	private String classSimpleName;
	private String className;
	
	private EntityManager em;
	
	public EAOQueryBuilder(Class<T> entityClass,EntityManager entityManager){
		this.entityClass = entityClass;
		this.className = this.entityClass.getName();
		this.classSimpleName = this.entityClass.getSimpleName();
		this.em = entityManager;
	}
	
	public Query build(EAOParams _params){
		
		EAOParams params = EAOQueryBuilder.QUERY_BUILDER_DEFAULTS.merge(_params);

		StringBuilder sb = new StringBuilder();
		
		boolean count = (Boolean) params.get(EAOQueryBuilder.COUNT_KEY);
		
		String cursor = count ? "count("+classSimpleName+")" 
				: classSimpleName;

		sb.append("select "+cursor+" from "+className+" "+classSimpleName+" ");
		sb.append("where  1=1 ");
		sb.append(buildConditionVariables(params.get(EAOQueryBuilder.CONDITIONS_KEY)));
		
		log.info("Conditions: "+params.get(EAOQueryBuilder.CONDITIONS_KEY));
		
		if(em==null){
			log.error("EntityManager CANNOT be NULL!!");
		}
		
		if(params.get("sort") instanceof String){
			
			sb.append(" order by "+classSimpleName+"."+params.get("sort")+" "+params.get("order"));
		}else if(params.get("sort") instanceof String[] && params.get("order") instanceof String){
			
			String[] sortFields = (String[]) params.get("sort");
			
			StringBuilder sortSQL = new StringBuilder();
			
			boolean firstRun = true;
			
			for(String sortField : sortFields){
				
				String prefix = ",";
				
				if(firstRun){
					prefix = "";
					firstRun = false;
				}
				
				sortSQL.append(prefix)
						.append(classSimpleName)
						.append(".")
						.append(sortField);
			}
			
			sb.append(" order by "+sortSQL.toString()+" "+params.get("order"));
			
		}else if(params.get("sort") instanceof String[] && params.get("order") instanceof String[]){
			
			String[] sortFields  = (String[]) params.get("sort");
			String[] orderFields = (String[]) params.get("order");
			
			StringBuilder sortSQL = new StringBuilder();
			
			int i = 0;
			
			for(String sortField : sortFields){
				
				String prefix = ",";
				
				if(i==0){
					prefix = "";
				}
				
				sortSQL.append(prefix)
						.append(classSimpleName)
						.append(".")
						.append(sortField)
						.append(" ")
						.append(orderFields[i]);
				
				i++;
			}
			
			sb.append(" order by "+sortSQL.toString()+" "+params.get("order"));
		}
		
		log.info("creating query with sql "+sb.toString());
		
		Query query = em.createQuery(sb.toString());
				
		setupConditionParameters(params.get(EAOQueryBuilder.CONDITIONS_KEY),query);
		
		
		
		
		if(!count){
			query.setMaxResults((Integer) params.get(EAOQueryBuilder.MAX_KEY));
			query.setFirstResult((Integer) params.get(EAOQueryBuilder.OFFSET_KEY));
		}
		
		return query;
		
	}
	
	private String buildConditionVariables(Object conditions){
		
		EAOParams params = null;
		
		if(conditions instanceof EAOParams){
			params = (EAOParams) conditions;
			
			final StringBuilder sb = new StringBuilder();
		
			params.iterate(new EAOParams.EAOParamsIterator() {
				
				@Override
				public void doInIteration(String key, Object value) {
					// TODO Auto-generated method stub
					sb.append(" and "+classSimpleName+"."+key+"=:"+key);
				}
			});
			
			return sb.toString();
			
		}else{
			return "";
		}
		
	}
	
	
	private Query setupConditionParameters(Object conditions,final Query query){
		
		EAOParams params = null;
		
		if(conditions instanceof EAOParams){
			params = (EAOParams) conditions;
			
			params.iterate(new EAOParams.EAOParamsIterator() {
				
				@Override
				public void doInIteration(String key, Object value) {
					// TODO Auto-generated method stub
					query.setParameter(key, value);
				}
			});
			
			log.info("Parameter values: "+params);
			
		}
		
		return query;
		
	}
	
	public static EAOParams QUERY_BUILDER_DEFAULTS = new EAOParams(){{
		put("count",false);
		put("max", 10);
		put("offset",0);
		put("conditions",new EAOParams());
		put("order","asc");
	}};
	
	
	
}
